package com.zhangzhan.gulimallsearch.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zhangzhan.gulimallcommcon.tos.es.SkuEsModel;
import com.zhangzhan.gulimallcommcon.util.MyCollUtil;
import com.zhangzhan.gulimallcommcon.util.R;
import com.zhangzhan.gulimallsearch.config.ElasticSearchConfig;
import com.zhangzhan.gulimallsearch.constant.EsConstant;
import com.zhangzhan.gulimallsearch.feign.ProductFeignService;
import com.zhangzhan.gulimallsearch.service.SearchService;
import com.zhangzhan.gulimallsearch.vos.AttrResponseVo;
import com.zhangzhan.gulimallsearch.vos.SearchParam;
import com.zhangzhan.gulimallsearch.vos.SearchResult;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.zhangzhan.gulimallsearch.constant.EsConstant.PAGE_SIZE;

/**
 * @author 张一恒
 * @version 1.0
 * @description:
 * @date 2024/6/23 4:10
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ProductFeignService productFeignService;

    @Override
    public SearchResult listPage(SearchParam searchParam) {
        //创建搜索请求 1.组装模糊搜索条件、过滤条件（精确过滤、区间过滤）、(2.排序、3.分页、4.高亮)、5.聚合搜索、收集组装后的请求对象、收集字段元素并返回

        //创建搜索请求
        SearchRequest searchRequest = new SearchRequest(EsConstant.PRODUCT_INDEX);


        //组装模糊搜索条件 组装模糊搜索条件、（精确过滤、区间过滤）、(2.排序、3.分页、4.高亮)、聚合搜索
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


        //构建请求DSL
        builderDSL(searchParam, searchSourceBuilder);

        //收集字段元素并返回
        SearchResult searchResult = new SearchResult();
        try {
            //收集组装后的请求对象
            searchRequest.source(searchSourceBuilder);

            SearchResponse search = restHighLevelClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
            SearchHits hits = search.getHits();
            searchResult = new SearchResult();

            //保存查询到的所有商品信息
            List<SkuEsModel> skuEsModels = new ArrayList<>();
            SearchHit[] data = hits.getHits();
            if (!Objects.isNull(data)) {
                for (SearchHit hit : data) {
                    String dataJson = hit.getSourceAsString();
                    SkuEsModel skuEsModel = JSON.parseObject(dataJson, SkuEsModel.class);
                    //设置高亮
                    if (StrUtil.isNotBlank(searchParam.getKeyword())) {
                        HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                        for (Text text : skuTitle.getFragments()) {
                            skuEsModel.setSkuTitle(text.toString());
                        }
                    }
                    skuEsModels.add(skuEsModel);
                }
                searchResult.setProduct(skuEsModels);
            }

            //保存分页数据
            TotalHits totalHits = hits.getTotalHits();
            searchResult.setTotal(totalHits.value);

            Integer pageNum = searchParam.getPageNum();
            searchResult.setPageNum(pageNum);

            searchResult.setTotalPages(((int) totalHits.value % PAGE_SIZE) == 0 ? (int) totalHits.value / PAGE_SIZE : ((int) totalHits.value / PAGE_SIZE + 1));

            List<Integer> list = new ArrayList<>();
            for (int i = 1; i <= searchResult.getTotalPages(); i++) {
                list.add(i);
            }
            searchResult.setTotalPagesList(list);

            //保存所有涉及到的品牌
            ParsedLongTerms brandAgg = search.getAggregations().get("brand_agg");
            List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
            List<SearchResult.BrandVo> brands = new ArrayList<>();
            if (MyCollUtil.isNotEmpty(buckets)) {
                for (Terms.Bucket bucket : buckets) {
                    SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
                    brandVo.setBrandId(Long.parseLong(bucket.getKey().toString()));

                    ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
                    if (!Objects.isNull(brandImgAgg)) {
                        List<? extends Terms.Bucket> brandImgAggBuckets = brandImgAgg.getBuckets();
                        if (MyCollUtil.isNotEmpty(brandImgAggBuckets)) {
                            for (Terms.Bucket brandImg : brandImgAggBuckets) {
                                brandVo.setBrandImg(brandImg.getKey().toString());
                            }
                        }
                    }


                    ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
                    if (!Objects.isNull(brandNameAgg)) {
                        List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
                        if (MyCollUtil.isNotEmpty(brandNameAggBuckets)) {
                            for (Terms.Bucket brandName : brandNameAggBuckets) {
                                brandVo.setBrandName(brandName.getKey().toString());
                            }
                        }
                    }

                    brands.add(brandVo);
                }
            }
            searchResult.setBrands(brands);

            //保存所有涉及到的分类
            ParsedLongTerms catalogAgg = search.getAggregations().get("catalog_agg");
            List<? extends Terms.Bucket> catalogAggBuckets = catalogAgg.getBuckets();
            List<SearchResult.CatalogVo> catalogs = new ArrayList<>();
            if (MyCollUtil.isNotEmpty(catalogAggBuckets)) {
                for (Terms.Bucket catalogAggBucket : catalogAggBuckets) {
                    SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                    catalogVo.setCatalogId((Long) catalogAggBucket.getKey());

                    ParsedStringTerms catalogNameAgg = catalogAggBucket.getAggregations().get("catalog_name_agg");
                    if (!Objects.isNull(catalogNameAgg)) {
                        List<? extends Terms.Bucket> catalogNameAggBuckets = catalogNameAgg.getBuckets();
                        if (MyCollUtil.isNotEmpty(catalogNameAggBuckets)) {
                            for (Terms.Bucket catalogNameAggBucket : catalogNameAggBuckets) {
                                catalogVo.setCatalogName(catalogNameAggBucket.getKey().toString());
                            }
                        }
                    }
                    catalogs.add(catalogVo);
                }
            }
            searchResult.setCatalogs(catalogs);

            //保存所有涉及到的商品属性
            ParsedNested attrAgg = search.getAggregations().get("attr_agg");
            ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attr_id_agg");
            List<SearchResult.AttrVo> attrs = new ArrayList<>();
            if (!Objects.isNull(attrIdAgg)) {
                List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
                if (MyCollUtil.isNotEmpty(attrIdAggBuckets)) {
                    for (Terms.Bucket attrIdAggBucket : attrIdAggBuckets) {
                        SearchResult.AttrVo attr = new SearchResult.AttrVo();
                        attr.setAttrId((Long) attrIdAggBucket.getKey());

                        ParsedStringTerms attrNameAgg = attrIdAggBucket.getAggregations().get("attr_name_agg");
                        List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
                        if (MyCollUtil.isNotEmpty(attrNameAggBuckets)) {
                            for (Terms.Bucket attrNameAggBucket : attrNameAggBuckets) {
                                attr.setAttrName(attrNameAggBucket.getKey().toString());
                            }
                        }

                        ParsedStringTerms attrValueAgg = attrIdAggBucket.getAggregations().get("attr_value_agg");
                        List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                        List<String> attrValue = new ArrayList<>();
                        if (MyCollUtil.isNotEmpty(attrValueAggBuckets)) {
                            for (Terms.Bucket attrValueAggBucket : attrValueAggBuckets) {
                                attrValue.add(attrValueAggBucket.getKey().toString());
                            }
                        }
                        attr.setAttrValue(attrValue);

                        attrs.add(attr);
                    }
                }
            }
            searchResult.setAttrs(attrs);

            //面包屑
            //属性
            List<SearchResult.NavVo> navVos = new ArrayList<>();
            if (MyCollUtil.isNotEmpty(searchParam.getAttrs())) {
                List<Long> attrIds = new ArrayList<>();
                List<SearchResult.NavVo> attrNavs = searchParam.getAttrs().stream().map(attr -> {

                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    String[] strings = attr.split("_");
                    navVo.setNavValue(strings[1]);

                    attrIds.add(Long.parseLong(strings[0]));
                    R r = productFeignService.attrInfo(Long.parseLong(strings[0]));
                    if (r.getCode() == 0) {
                        String json = r.getData();
                        AttrResponseVo attrResponseVo = JSON.parseObject(json, AttrResponseVo.class);
                        navVo.setNavName(attrResponseVo.getAttrName());
                    } else {
                        navVo.setNavName(strings[0]);
                    }

                    replaceUri(searchParam, attr, navVo, "attrs");
                    return navVo;
                }).toList();
                if (MyCollUtil.isNotEmpty(attrNavs)) {
                    navVos.addAll(attrNavs);
                }
                searchResult.setAttrIds(attrIds);
            }

            //品牌
            if (MyCollUtil.isNotEmpty(searchParam.getBrandId())) {
                for (Long brandId : searchParam.getBrandId()) {
                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    navVo.setNavName("品牌");
                    R r = productFeignService.brandInfo(brandId);
                    if (r.getCode() == 0) {
                        String json = r.getData();
                        Map map = JSON.parseObject(json, Map.class);
                        navVo.setNavValue(map.get("name").toString());
                    } else {
                        navVo.setNavValue("");
                    }

                    replaceUri(searchParam, brandId.toString(), navVo, "brandId");

                    navVos.add(navVo);
                }
            }


            searchResult.setNavVos(navVos);
        } catch (Exception ignored) {
        }

        return searchResult;
    }

    private void replaceUri(SearchParam searchParam, String val, SearchResult.NavVo navVo, String key) {
        if (StrUtil.isNotBlank(searchParam.get_uri())) {
            val = URLEncoder.encode(val, StandardCharsets.UTF_8);
            val = val.replaceAll("\\+", "%20");
            String link = searchParam.get_uri().replaceAll("&" + key + "=" + val, "");

            navVo.setLink("http://search.gulimall.com/list.html?" + link);
        } else {
            navVo.setLink("http://search.gulimall.com/list.html");
        }
    }

    private void builderDSL(SearchParam searchParam, SearchSourceBuilder searchSourceBuilder) {
        //region 1.组装模糊搜索条件、（精确过滤、区间过滤）
        //整体查询 用于匹配与其他查询的布尔组合匹配
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //组装模糊搜索/全文搜索
        if (StrUtil.isNotBlank(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }

        //（精确过滤、区间过滤）
        //商品id
        if (MyCollUtil.isNotEmpty(searchParam.getBrandId())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
        }

        //分类id
        if (!Objects.isNull(searchParam.getCatalog3Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
        }

        //是否有货hasStock
        if (!Objects.isNull(searchParam.getHasStock())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
        }

        //价格区间  0_500,_500,500_
        if (StrUtil.isNotBlank(searchParam.getSkuPrice())) {
            String skuPrice = searchParam.getSkuPrice();
            String[] price = skuPrice.split("_");
            if (skuPrice.startsWith("_")) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(price[1]));
            } else if (skuPrice.endsWith("_")) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(price[0]));
            } else {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(price[0]));
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(price[1]));
            }
        }

        //attrs  1_5寸:8寸
        if (MyCollUtil.isNotEmpty(searchParam.getAttrs())) {
            for (String attr : searchParam.getAttrs()) {
                BoolQueryBuilder nestededBoolQueryBuilder = QueryBuilders.boolQuery();
                //组装attrId
                String attrId = attr.split("_")[0];
                nestededBoolQueryBuilder.filter(QueryBuilders.termQuery("attrs.attrId", attrId));
                //组装attrValue
                String attrValues = attr.split("_")[1];
                String[] values = attrValues.split(":");
                nestededBoolQueryBuilder.filter(QueryBuilders.termsQuery("attrs.attrValue", values));


                NestedQueryBuilder nesteded = QueryBuilders.nestedQuery("attrs", nestededBoolQueryBuilder, ScoreMode.None);
                boolQueryBuilder.filter(nesteded);
            }
        }


        searchSourceBuilder.query(boolQueryBuilder);
        //endregion


        //region (2.排序、3.分页、4.高亮)
        //排序 sort=price/saleCount/hotScore_desc/asc
        if (StrUtil.isNotBlank(searchParam.getSort())) {
            String sort = searchParam.getSort();
            String sortName = sort.split("_")[0];
            String sortOrder = sort.split("_")[1];
            SortOrder order = sortOrder.equals("asc") ? SortOrder.ASC : SortOrder.DESC;
            searchSourceBuilder.sort(sortName, order);
        }

        //分页
        searchSourceBuilder.from((searchParam.getPageNum() - 1) * PAGE_SIZE);
        searchSourceBuilder.size(PAGE_SIZE);

        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("skuTitle");
        highlightBuilder.preTags("<b style='color:red'>");
        highlightBuilder.postTags("</b>");
        searchSourceBuilder.highlighter(highlightBuilder);
        //endregion

        //region 聚合搜索
        //品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
        brand_agg.field("brandId").size(50)
                //品牌名称
                .subAggregation(AggregationBuilders.terms("brand_name_agg")
                        .field("brandName").size(50))
                //品牌图片
                .subAggregation(AggregationBuilders.terms("brand_img_agg")
                        .field("brandImg").size(50));
        searchSourceBuilder.aggregation(brand_agg);

        //分类聚合
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg");
        catalog_agg.field("catalogId").size(1)
                //分类名称
                .subAggregation(AggregationBuilders.terms("catalog_name_agg")
                        .field("catalogName").size(1));
        searchSourceBuilder.aggregation(catalog_agg);


        //属性聚合
        NestedAggregationBuilder nested = AggregationBuilders.nested("attr_agg", "attrs");
        nested.subAggregation(AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(50)
                //属性名
                .subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(50))
                //属性值
                .subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50)));
        searchSourceBuilder.aggregation(nested);
        //endregion

        System.out.println(searchSourceBuilder.toString());
    }
}
